package aceim.protocol.snuk182.icq.inner.dataprocessing; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Date; import java.util.List; import aceim.protocol.snuk182.icq.inner.ICQConstants; import aceim.protocol.snuk182.icq.inner.ICQException; import aceim.protocol.snuk182.icq.inner.ICQServiceInternal; import aceim.protocol.snuk182.icq.inner.ICQServiceResponse; import aceim.protocol.snuk182.icq.inner.dataentity.Flap; import aceim.protocol.snuk182.icq.inner.dataentity.ICQOnlineInfo; import aceim.protocol.snuk182.icq.inner.dataentity.ServiceRedirect; import aceim.protocol.snuk182.icq.inner.dataentity.Snac; import aceim.protocol.snuk182.icq.inner.dataentity.TLV; import aceim.protocol.snuk182.icq.utils.ProtocolUtils; public class MainProcessor extends AbstractFlapProcessor { @Override public void init(ICQServiceInternal service) throws ICQException{ super.init(service); service.log("---------------main thread---------------"); goOnline(); } private void goOnline() throws ICQException{ Flap[] flaps = new Flap[]{ sendOnlineStatusAndDCInfo(), sendOnlineReady(), /*service.getMessagingEngine().getOfflineMessagesRequestFlap(), */ requestOfflineMessages()}; service.getServiceResponse().respond(ICQServiceResponse.RES_CONNECTING, 9); service.getRunnableService().sendMultipleToSocket(flaps); service.getPersonalInfoEngine().getShortPersonalMetainfo(service.getUn()); if (service.getCurrentState() != ICQServiceInternal.STATE_CONNECTED){ service.setCurrentState(ICQServiceInternal.STATE_CONNECTED); service.getServiceResponse().respond(ICQServiceResponse.RES_CONNECTED); } //ICQOnlineInfo onlineInfo = service.getOnlineInfo(); //if (onlineInfo.extendedStatusId > -1) { sendStatusChange(service.getOnlineInfo()); //} } private Flap requestOfflineMessages() throws ICQException{ Flap flap = new Flap(); flap.channel = ICQConstants.FLAP_CHANNELL_DATA; Snac data = new Snac(); data.serviceId = ICQConstants.SNAC_FAMILY_ICQEXTENSION; data.subtypeId = ICQConstants.SNAC_ICQEXTENSION_METAINFOREQ; data.requestId = ICQConstants.SNAC_ICQEXTENSION_METAINFOREQ; TLV tlv = new TLV(); tlv.type = ICQConstants.TLV_ICQEXTENSION_METADATA; byte[] tlvData = new byte[10]; tlvData[0] = 0x8; tlvData[1] = 0; byte[] uinIntBytes = ProtocolUtils.int2ByteLE(Integer.parseInt(service.getUn())); System.arraycopy(uinIntBytes, 0, tlvData, 2, 4); System.arraycopy(ProtocolUtils.short2ByteLE(ICQConstants.ICQEXTENSION_COMMAND_GETOFFLINEMESSAGES), 0, tlvData, 6, 2); System.arraycopy(ProtocolUtils.short2ByteLE((short) 0x0), 0, tlvData, 8, 2); tlv.value = tlvData; data.data = new TLV[]{tlv}; flap.data = data; return flap; } @Override protected void internalFlapMap(Flap flap)throws ICQException{ if (flap == null) return; switch (flap.channel){ case ICQConstants.FLAP_CHANNELL_START: break; case ICQConstants.FLAP_CHANNELL_DATA: internalSnacMap(flap.data); break; case ICQConstants.FLAP_CHANNELL_CLOSE: if (flap.tlvData!=null){ for (int i=0; i<flap.tlvData.length; i++){ TLV tlv = flap.tlvData[i]; internalTLVMap(tlv); } } service.lastConnectionError = "multiple login"; service.getRunnableService().disconnect(); break; } } @Override protected void internalTLVMap(TLV tlv) throws ICQException{ // TODO Auto-generated method stub } @Override protected void internalSnacMap(Snac snac)throws ICQException{ if (snac == null) return; switch(snac.getServiceId()){ case ICQConstants.SNAC_FAMILY_GENERIC: switch(snac.subtypeId){ case ICQConstants.SNAC_GENERIC_REDIRECT: service.log("redirect got"); parseRedirectInfo(snac); break; case ICQConstants.SNAC_GENERIC_RATELIMITWARNING: service.log("rate limit warning!"); break; case ICQConstants.SNAC_GENERIC_SERVERPAUSE: service.log("server pause!"); break; case ICQConstants.SNAC_GENERIC_OWNINFORES: service.getOnlineInfoEngine().parseOnlineInfo(snac, service.getOnlineInfo()); service.getServiceResponse().respond(ICQServiceResponse.RES_ACCOUNTUPDATED, service.getOnlineInfo()); case ICQConstants.SNAC_GENERIC_EXTSTATUSRES: parseExternalStatus(snac); break; } break; case ICQConstants.SNAC_FAMILY_LOCATION: switch(snac.subtypeId){ case ICQConstants.SNAC_LOCATION_PARAMRES: try { snac.data = service.getDataParser().parseTLV(snac.plainData); } catch (ICQException e) { service.log(e); } break; case ICQConstants.SNAC_LOCATION_ERROR: case ICQConstants.SNAC_LOCATION_XZRES: service.getServiceResponse().respond(ICQServiceResponse.RES_KEEPALIVE); break; } break; case ICQConstants.SNAC_FAMILY_BUDDYLISTMGMT: switch(snac.subtypeId){ case ICQConstants.SNAC_BUDDYLISTMGMT_PARAMRES: try { snac.data = service.getDataParser().parseTLV(snac.plainData); } catch (ICQException e) { service.log(e); } break; case ICQConstants.SNAC_BUDDYLISTMGMT_USERONLINE: case ICQConstants.SNAC_BUDDYLISTMGMT_USEROFFLINE: parseBuddyInfo(snac); break; } break; case ICQConstants.SNAC_FAMILY_MESSAGING: switch(snac.subtypeId){ case ICQConstants.SNAC_MESSAGING_PARAMRES: break; case ICQConstants.SNAC_MESSAGING_MSGSENTTHROUGHSERVER: service.getMessagingEngine().parseMessage(snac); break; case ICQConstants.SNAC_MESSAGING_PLUGINMSG: service.getMessagingEngine().parsePluginMessage(snac, false); break; case ICQConstants.SNAC_MESSAGING_MSGSENT: service.getMessagingEngine().parsePluginMessage(snac, true); break; case ICQConstants.SNAC_MESSAGING_TYPINGNOTIFICATION: service.getMessagingEngine().parseTyping(snac); break; } break; case ICQConstants.SNAC_FAMILY_SERVERSIDEINFO: switch(snac.subtypeId){ case ICQConstants.SNAC_SERVERSIDEINFO_CLRES: parseBuddyList(snac.plainData, snac.lFlag==1); break; case ICQConstants.SNAC_SERVERSIDEINFO_SSIEDITRES: service.getSSIEngine().parseSSIResponse(snac.plainData); break; case ICQConstants.SNAC_SERVERSIDEINFO_AUTHRES: service.getSSIEngine().parseAuthResponse(snac.plainData); break; case ICQConstants.SNAC_SERVERSIDEINFO_AUTHREQ: service.getSSIEngine().parseAuthRequest(snac.plainData); break; } break; case ICQConstants.SNAC_FAMILY_ICQEXTENSION: switch(snac.subtypeId){ case ICQConstants.SNAC_ICQEXTENSION_METAINFORES: byte[] data = snac.plainData; try { TLV[] tlvs = service.getDataParser().parseTLV(data); for (TLV tlv:tlvs){ switch(tlv.type){ case ICQConstants.TLV_ICQEXTENSION_METADATA: parseICQExtensionMetaInfo(tlv.value, snac.lFlag < 1); break; } } } catch (ICQException e) { service.log(e); } break; } break; } } private void parseRedirectInfo(Snac snac) throws ICQException{ ServiceRedirect redirect = null; byte[] tlvData = snac.plainData; try { redirect = new ServiceRedirect(service.getDataParser().parseTLV(tlvData)); } catch (ICQException e) { service.log(e); } if (redirect == null) return; switch(redirect.family){ case ICQConstants.SNAC_FAMILY_SERVERSTOREDBUDDYICON: service.getBuddyIconEngine().serviceServerURLResponse(redirect); break; } } private void parseBuddyInfo(Snac snac) throws ICQException{ List<ICQOnlineInfo> infos = service.getOnlineInfoEngine().parseOnlineInfo(snac, null); if (infos == null) return; for(ICQOnlineInfo info:infos){ for (int i=service.getBuddyList().buddyInfos.size()-1; i>-1; i--){ ICQOnlineInfo nfo = service.getBuddyList().buddyInfos.get(i); if (nfo.uin.equals(info.uin)){ service.getBuddyList().buddyInfos.remove(i); } } if (snac.getServiceId()==ICQConstants.SNAC_FAMILY_BUDDYLISTMGMT && snac.subtypeId==ICQConstants.SNAC_BUDDYLISTMGMT_USEROFFLINE){ info.userStatus = ICQConstants.STATUS_OFFLINE; } if (snac.getServiceId()==ICQConstants.SNAC_FAMILY_BUDDYLISTMGMT && snac.subtypeId==ICQConstants.SNAC_BUDDYLISTMGMT_USERONLINE && info.userStatus == ICQConstants.STATUS_OFFLINE){ info.userStatus = ICQConstants.STATUS_ONLINE; } if (snac.getServiceId()==ICQConstants.SNAC_FAMILY_BUDDYLISTMGMT){ service.getBuddyList().buddyInfos.add(info); } service.getServiceResponse().respond(ICQServiceResponse.RES_BUDDYSTATECHANGED, info); } } private void parseICQExtensionMetaInfo(byte[] data, boolean last) throws ICQException{ //2 bytes - length-2 - useless long receiverUinInt = ProtocolUtils.unsignedInt2Long(ProtocolUtils.bytes2IntLE(data, 2)); String receiverUin = new String(receiverUinInt+""); if (!receiverUin.equalsIgnoreCase(service.getUn())){ // lol:) service.log(" hohoho, message sent to "+receiverUin); } short metaInfoType = ProtocolUtils.bytes2ShortLE(data, 6); @SuppressWarnings("unused") short seqNumber = ProtocolUtils.bytes2ShortLE(data, 8); switch(metaInfoType){ case 0x41: service.log(" got offline message"); byte[] tailData = new byte[data.length-10]; System.arraycopy(data, 10, tailData, 0, tailData.length); service.getMessagingEngine().parseOfflineMessage(tailData); break; case 0x42: service.log(" no more offline messages"); service.getRunnableService().sendToSocket(requestDeleteOfflineMessagesFromServer()); break; case 0x7da: tailData = new byte[data.length-10]; System.arraycopy(data, 10, tailData, 0, tailData.length); String uin = service.getPersonalInfoEngine().metaInfoRequestMap.remove(new Byte(data[9])); service.getPersonalInfoEngine().parsePersonalInfoResponse(uin, tailData, last); break; } } private Flap requestDeleteOfflineMessagesFromServer() throws ICQException{ Flap flap = new Flap(); flap.channel = ICQConstants.FLAP_CHANNELL_DATA; Snac data = new Snac(); data.serviceId = ICQConstants.SNAC_FAMILY_ICQEXTENSION; data.subtypeId = ICQConstants.SNAC_ICQEXTENSION_METAINFOREQ; data.requestId = ICQConstants.SNAC_ICQEXTENSION_METAINFOREQ; TLV tlv = new TLV(); tlv.type = ICQConstants.TLV_ICQEXTENSION_METADATA; byte[] tlvData = new byte[10]; System.arraycopy(ProtocolUtils.short2ByteLE((short) 8), 0, tlvData, 0, 2); System.arraycopy(ProtocolUtils.int2ByteLE(Integer.parseInt(service.getUn())), 0, tlvData, 2, 4); System.arraycopy(ProtocolUtils.short2ByteLE(ICQConstants.ICQEXTENSION_COMMAND_DELETEOFFLINEMESSAGES), 0, tlvData, 6, 2); System.arraycopy(ProtocolUtils.short2ByteLE((short) 0), 0, tlvData, 8, 2); tlv.value = tlvData; data.data = new TLV[]{tlv}; flap.data = data; return flap; } /*public void sendKeepalive(){ if (keepaliveTimer.isAlive()){ //keepaliveTimer.reset = true; }else { keepaliveTimer.start(); } }*/ private Flap sendOnlineReady() throws ICQException{ Flap flap = new Flap(); flap.channel = ICQConstants.FLAP_CHANNELL_DATA; Snac data = new Snac(); data.serviceId = ICQConstants.SNAC_FAMILY_GENERIC; data.subtypeId = ICQConstants.SNAC_GENERIC_CLIENTONLINE; data.requestId = ICQConstants.SNAC_GENERIC_CLIENTONLINE; byte[] families = new byte[ICQConstants.LOCAL_FAMILIES_VERSIONS.length*2]; for (int i=0; i<ICQConstants.LOCAL_FAMILIES_VERSIONS.length/4; i++){ System.arraycopy(ICQConstants.LOCAL_FAMILIES_VERSIONS, i*4, families, i*8, 4); System.arraycopy(ProtocolUtils.short2ByteBE(ICQConstants.FAMILY_TOOL_ID), 0, families, (i*8)+4, 2); System.arraycopy(ProtocolUtils.short2ByteBE(ICQConstants.FAMILY_TOOL_VERSION), 0, families, (i*8)+6, 2); } data.plainData = families; flap.data = data; return flap; } private Flap sendOnlineStatusAndDCInfo() throws ICQException{ TLV dcinfo = new TLV(); dcinfo.type = 0xc; byte[] rawInfo = new byte[37]; byte[] buffer; try { buffer = InetAddress.getLocalHost().getAddress(); } catch (UnknownHostException e) { buffer = new byte[]{0,0,0,0}; } System.arraycopy(buffer, 0, rawInfo, 0, 4); buffer = new byte[]{0,0,(byte) 0xab, (byte) 0xcd}; System.arraycopy(buffer, 0, rawInfo, 4, 4); rawInfo[8] = ICQConstants.DC_NORMAL; buffer = ProtocolUtils.short2ByteBE(ICQConstants.DC_PROTO_VERSION); System.arraycopy(buffer, 0, rawInfo, 9, 2); buffer = ProtocolUtils.int2ByteLE((int) new Date().getTime()/1000); System.arraycopy(buffer, 0, rawInfo, 11, 4); buffer = ProtocolUtils.int2ByteBE(ICQConstants.WEB_FRONT_PORT); System.arraycopy(buffer, 0, rawInfo, 15, 4); System.arraycopy(new byte[]{0,0,0,3}, 0, rawInfo, 19, 4); buffer = ProtocolUtils.int2ByteBE((int) new Date().getTime()/1000); System.arraycopy(buffer, 0, rawInfo, 23, 4); buffer = ProtocolUtils.int2ByteBE((int) new Date().getTime()/1000); System.arraycopy(buffer, 0, rawInfo, 27, 4); System.arraycopy(new byte[]{0,0,0,0}, 0, rawInfo, 31, 4); System.arraycopy(new byte[]{0,0}, 0, rawInfo, 35, 2); dcinfo.value = rawInfo; if (service.getOnlineInfo().extendedStatusId>-1){ return preparePlainStatusChange(service.getOnlineInfo().userStatus, new TLV[]{dcinfo, getICQMoodTLV()}); } else { return preparePlainStatusChange(service.getOnlineInfo().userStatus, new TLV[]{dcinfo}); } } /*class KeepaliveTimer extends Thread{ public volatile boolean running = true; @Override public void run(){ while(running){ try { Thread.sleep(120000); if (running){ bugogaAttacks(); } //sendKeepalive(); //checkServerReachable(); } catch (InterruptedException e) { service.log(e); } } } }*/ public void sendStatusChange(int newStatus){ service.getRunnableService().sendMultipleToSocket(new Flap[]{preparePlainStatusChange(newStatus, null)}); } private Flap preparePlainStatusChange(int newStatus, TLV[] additionalTLVS){ Flap flap2 = new Flap(); flap2.channel = ICQConstants.FLAP_CHANNELL_DATA; Snac data2 = new Snac(); data2.serviceId = ICQConstants.SNAC_FAMILY_GENERIC; data2.subtypeId = ICQConstants.SNAC_GENERIC_STATUSSET; data2.requestId = ICQConstants.SNAC_GENERIC_STATUSSET; if (additionalTLVS!=null && additionalTLVS.length>0){ TLV[] tlvs = new TLV[additionalTLVS.length+1]; tlvs[0] = getStatusChangeTLV(newStatus); for (int i=0; i<additionalTLVS.length; i++){ tlvs[i+1] = additionalTLVS[i]; } data2.data = tlvs; } else { data2.data = new TLV[]{getStatusChangeTLV(newStatus)}; } flap2.data = data2; return flap2; } private TLV getICQMoodTLV(){ TLV statusText = new TLV(); statusText.type = ICQConstants.TLV_ONLINE_ICONDATA; String fullText = service.getOnlineInfo().personalText+" "+service.getOnlineInfo().extendedStatus; if (fullText.length()>250){ fullText = fullText.substring(0, 250); } byte[] textBytes; try { textBytes = fullText.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { textBytes = fullText.getBytes(); } byte[] icqmood = new String("icqmood"+service.getOnlineInfo().extendedStatusId).getBytes(); byte[] textData = new byte[12+textBytes.length+icqmood.length]; int i=0; System.arraycopy(ProtocolUtils.short2ByteBE((short) 2), 0, textData, i, 2); i+=2; textData[i] = 4; i++; textData[i] = (byte) (textBytes.length+4); i++; System.arraycopy(ProtocolUtils.short2ByteBE((short) textBytes.length), 0, textData, i, 2); i+=2; System.arraycopy(textBytes, 0, textData, i, textBytes.length); i+=textBytes.length; System.arraycopy(ProtocolUtils.int2ByteBE(0xe), 0, textData, i, 4); i+=4; System.arraycopy(ProtocolUtils.short2ByteBE((short) icqmood.length), 0, textData, i, 2); i+=2; System.arraycopy(icqmood, 0, textData, i, icqmood.length); i+=icqmood.length; statusText.value = textData; return statusText; } public void sendStatusChange(ICQOnlineInfo onlineInfo) { Flap flap1 = preparePlainStatusChange(onlineInfo.userStatus, new TLV[]{getICQMoodTLV()}); Flap flap2 = new Flap(); flap2.channel = ICQConstants.FLAP_CHANNELL_DATA; Snac data2 = new Snac(); data2.serviceId = ICQConstants.SNAC_FAMILY_LOCATION; data2.subtypeId = ICQConstants.SNAC_LOCATION_USERINFOSET; data2.requestId = ICQConstants.SNAC_LOCATION_USERINFOSET; TLV clsids = new TLV(); clsids.type = 0x5; byte[] rawData = new byte[(16*10) + (onlineInfo.qipStatus!=null?16:0) + (onlineInfo.extendedStatusId>-1?16:0)]; int i = 0; System.arraycopy(ICQConstants.CLSID_UTF, 0, rawData, i, 16); i+=16; System.arraycopy(ICQConstants.CLSID_RTF, 0, rawData, i, 16); i+=16; System.arraycopy(ICQConstants.CLSID_DIRECT, 0, rawData, i, 16); i+=16; System.arraycopy(ICQConstants.CLSID_SRV_RELAY, 0, rawData, i, 16); i+=16; System.arraycopy(ICQConstants.CLSID_CLIENTINFOPREFIX, 0, rawData, 64, 16); i+=16; /*System.arraycopy(new String("Asia 0.5.0.0.001").getBytes(), 0, rawData, i, 16); i+=16;*/ System.arraycopy(ICQConstants.CLSID_TYPING, 0, rawData, i, 16); i+=16; System.arraycopy(ICQConstants.CLSID_AIM_FILERECEIVE, 0, rawData, i, 16); i+=16; System.arraycopy(ICQConstants.CLSID_AIM_FILESEND, 0, rawData, i, 16); i+=16; System.arraycopy(ICQConstants.CLSID_ASIA, 0, rawData, i, 16); i+=16; System.arraycopy(ICQConstants.CLSID_XTRAZ, 0, rawData, i, 16); i+=16; if (onlineInfo.extendedStatusId>-1){ System.arraycopy(ICQConstants.XSTATUS_CLSIDS[onlineInfo.extendedStatusId], 0, rawData, i, 16); i+=16; } if (onlineInfo.qipStatus!=null){ System.arraycopy(onlineInfo.qipStatus, 0, rawData, i, 16); i+=16; } clsids.value = rawData; data2.data = new TLV[]{clsids}; flap2.data = data2; service.getRunnableService().sendMultipleToSocket(new Flap[]{flap1, flap2}); } private TLV getStatusChangeTLV(int statusValue){ TLV status = new TLV(); status.type = 0x6; byte[] rawStatus = new byte[4]; byte[] dcauth = ProtocolUtils.short2ByteBE((short) ICQConstants.STATUS_DCAUTH); byte[] online = ProtocolUtils.short2ByteBE((short) statusValue); System.arraycopy(dcauth, 0, rawStatus, 0, 2); System.arraycopy(online, 0, rawStatus, 2, 2); status.value = rawStatus; return status; } @Override public void onDisconnect() {} public void checkServerConnection() { Flap flap = new Flap(); flap.channel = ICQConstants.FLAP_CHANNELL_DATA; Snac data = new Snac(); data.serviceId = ICQConstants.SNAC_FAMILY_LOCATION; data.subtypeId = ICQConstants.SNAC_LOCATION_XZREQ; data.requestId = ICQConstants.SNAC_LOCATION_XZREQ; byte[] uidBytes; try { uidBytes = service.getUn().getBytes("ASCII"); } catch (UnsupportedEncodingException e) { uidBytes = service.getUn().getBytes(); } byte[] allBytes = new byte[uidBytes.length + 1]; allBytes[0] = (byte) uidBytes.length; System.arraycopy(uidBytes, 0, allBytes, 1, uidBytes.length); data.plainData = allBytes; flap.data = data; service.getRunnableService().sendToSocket(flap); } protected void parseExternalStatus(Snac snac){ byte[] data = snac.plainData; short action = ProtocolUtils.bytes2ShortBE(data, 0); if (action == 0 || action == 1) { byte flags = data[2]; if ((flags & 0x40) > 0) { service.log("Upload request"); service.getBuddyIconEngine().requestIconUpload(service.getSSIEngine().newIcon); } else { //we've just deleted old icon if (data[3] == 0 && service.getOnlineInfo().iconData != null) { service.getOnlineInfo().iconData = null; service.getSSIEngine().requestIconUpload(service.getSSIEngine().newIcon); } } } } }